/*
 * objectaccess.h
 *
 *        Object access hooks.
 *
 * Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group
 * Portions Copyright (c) 1994, Regents of the University of California
 */

#ifndef OBJECTACCESS_H
#define OBJECTACCESS_H

/*
 * Object access hooks are intended to be called just before or just after
 * performing certain actions on a SQL object.  This is intended as
 * infrastructure for security or logging plugins.
 *
 * OAT_POST_CREATE should be invoked just after the object is created.
 * Typically, this is done after inserting the primary catalog records and
 * associated dependencies.
 *
 * OAT_DROP should be invoked just before deletion of objects; typically
 * deleteOneObject(). Its arguments are packed within ObjectAccessDrop.
 *
 * OAT_POST_ALTER should be invoked just after the object is altered,
 * but before the command counter is incremented.  An extension using the
 * hook can use a current MVCC snapshot to get the old version of the tuple,
 * and can use SnapshotSelf to get the new version of the tuple.
 *
 * OAT_NAMESPACE_SEARCH should be invoked prior to object name lookup under
 * a particular namespace. This event is equivalent to usage permission
 * on a schema under the default access control mechanism.
 *
 * OAT_FUNCTION_EXECUTE should be invoked prior to function execution.
 * This event is almost equivalent to execute permission on functions,
 * except for the case when execute permission is checked during object
 * creation or altering, because OAT_POST_CREATE or OAT_POST_ALTER are
 * sufficient for extensions to track these kind of checks.
 *
 * Other types may be added in the future.
 */
typedef enum ObjectAccessType
{
    OAT_POST_CREATE,
    OAT_DROP,
    OAT_POST_ALTER,
    OAT_NAMESPACE_SEARCH,
    OAT_FUNCTION_EXECUTE
} ObjectAccessType;

/*
 * Arguments of OAT_POST_CREATE event
 */
typedef struct
{
    /*
     * This flag informs extensions whether the context of this creation is
     * invoked by user's operations, or not. E.g, it shall be dealt as
     * internal stuff on toast tables or indexes due to type changes.
     */
    bool        is_internal;
} ObjectAccessPostCreate;

/*
 * Arguments of OAT_DROP event
 */
typedef struct
{
    /*
     * Flags to inform extensions the context of this deletion. Also see
     * PERFORM_DELETION_* in dependency.h
     */
    int            dropflags;
} ObjectAccessDrop;

/*
 * Arguments of OAT_POST_ALTER event
 */
typedef struct
{
    /*
     * This identifier is used when system catalog takes two IDs to identify a
     * particular tuple of the catalog. It is only used when the caller want
     * to identify an entry of pg_inherits, pg_db_role_setting or
     * pg_user_mapping. Elsewhere, InvalidOid should be set.
     */
    Oid            auxiliary_id;

    /*
     * If this flag is set, the user hasn't requested that the object be
     * altered, but we're doing it anyway for some internal reason.
     * Permissions-checking hooks may want to skip checks if, say, we're alter
     * the constraints of a temporary heap during CLUSTER.
     */
    bool        is_internal;
} ObjectAccessPostAlter;

/*
 * Arguments of OAT_NAMESPACE_SEARCH
 */
typedef struct
{
    /*
     * If true, hook should report an error when permission to search this
     * schema is denied.
     */
    bool        ereport_on_violation;

    /*
     * This is, in essence, an out parameter.  Core code should initialize
     * this to true, and any extension that wants to deny access should reset
     * it to false.  But an extension should be careful never to store a true
     * value here, so that in case there are multiple extensions access is
     * only allowed if all extensions agree.
     */
    bool        result;
} ObjectAccessNamespaceSearch;

/* Plugin provides a hook function matching this signature. */
typedef void (*object_access_hook_type) (ObjectAccessType access,
                                         Oid classId,
                                         Oid objectId,
                                         int subId,
                                         void *arg);

/* Plugin sets this variable to a suitable hook function. */
extern PGDLLIMPORT object_access_hook_type object_access_hook;

/* Core code uses these functions to call the hook (see macros below). */
extern void RunObjectPostCreateHook(Oid classId, Oid objectId, int subId,
                        bool is_internal);
extern void RunObjectDropHook(Oid classId, Oid objectId, int subId,
                  int dropflags);
extern void RunObjectPostAlterHook(Oid classId, Oid objectId, int subId,
                       Oid auxiliaryId, bool is_internal);
extern bool RunNamespaceSearchHook(Oid objectId, bool ereport_on_volation);
extern void RunFunctionExecuteHook(Oid objectId);

/*
 * The following macros are wrappers around the functions above; these should
 * normally be used to invoke the hook in lieu of calling the above functions
 * directly.
 */

#define InvokeObjectPostCreateHook(classId,objectId,subId)            \
    InvokeObjectPostCreateHookArg((classId),(objectId),(subId),false)
#define InvokeObjectPostCreateHookArg(classId,objectId,subId,is_internal) \
    do {                                                            \
        if (object_access_hook)                                        \
            RunObjectPostCreateHook((classId),(objectId),(subId),    \
                                    (is_internal));                    \
    } while(0)

#define InvokeObjectDropHook(classId,objectId,subId)                \
    InvokeObjectDropHookArg((classId),(objectId),(subId),0)
#define InvokeObjectDropHookArg(classId,objectId,subId,dropflags)    \
    do {                                                            \
        if (object_access_hook)                                        \
            RunObjectDropHook((classId),(objectId),(subId),            \
                              (dropflags));                            \
    } while(0)

#define InvokeObjectPostAlterHook(classId,objectId,subId)            \
    InvokeObjectPostAlterHookArg((classId),(objectId),(subId),        \
                                 InvalidOid,false)
#define InvokeObjectPostAlterHookArg(classId,objectId,subId,        \
                                     auxiliaryId,is_internal)        \
    do {                                                            \
        if (object_access_hook)                                        \
            RunObjectPostAlterHook((classId),(objectId),(subId),    \
                                   (auxiliaryId),(is_internal));    \
    } while(0)

#define InvokeNamespaceSearchHook(objectId, ereport_on_violation)    \
    (!object_access_hook                                            \
     ? true                                                            \
     : RunNamespaceSearchHook((objectId), (ereport_on_violation)))

#define InvokeFunctionExecuteHook(objectId)        \
    do {                                        \
        if (object_access_hook)                    \
            RunFunctionExecuteHook(objectId);    \
    } while(0)

#endif                            /* OBJECTACCESS_H */
